package tablero;

import java.util.ArrayList;
import java.util.Random;
import org.uqbar.commons.model.UserException;

import org.uqbar.commons.model.ObservableObject;

public class Tablero extends ObservableObject {

	public static final String LIMITE = "limite";
	public static final String ESFERAS = "esferas";
	public static final String PERSONAJES = "personajes";
	public static final String TABLEROIMPRESO = "tableroImpreso";
	public static final String RENDERESFERAS = "renderEsferas";
	public static final String SELECTED = "selected";
	public static final String ESFERASELECTED = "esferaSelected";
	public static final String PERSONAJESELECTED = "personajeSelected";
	public static final String QUITARELEMENTO = "quitarElemento";

	private Posicionable selected;
	private Esfera esferaSelected;
	private Personaje personajeSelected;

	private Coordenada limite;
	private ArrayList<Esfera> esferas;
	private ArrayList<Personaje> personajes;
	private TableroPrint tableroImpresor;
	private String tableroImpreso;
	private ArrayList<Posicionable> elementos = new ArrayList<Posicionable>();

	public Posicionable getSelected() {
		return selected;
	}

	public void setSelected(Posicionable selected) {
		this.selected = selected;
	}

	public Coordenada getLimite() {
		return limite;
	}

	public void setLimite(Coordenada limite) {
		this.limite = limite;
	}

	public ArrayList<Posicionable> getElementos() {
		return elementos;
	}

	public void setElementos(ArrayList<Posicionable> es) {
		this.elementos = es;
	}

	public ArrayList<Esfera> getEsferas() {
		return esferas;
	}

	public void setEsferas(ArrayList<Esfera> esferas) {
		this.esferas = esferas;
	}

	public ArrayList<Personaje> getPersonajes() {
		return personajes;
	}

	public void setPersonajes(ArrayList<Personaje> personajes) {
		this.personajes = personajes;

	}

	public TableroPrint getTableroImpresor() {
		return tableroImpresor;
	}

	public void setTableroImpresor(TableroPrint tableroImpresor) {
		this.setProperty("tableroImpresor", tableroImpresor);
	}

	public String getTableroImpreso() {
		return tableroImpreso;

	}

	public void setTableroImpreso(String tableroImpreso) {
		this.setProperty(TABLEROIMPRESO, tableroImpreso);
		this.firePropertyChange(TABLEROIMPRESO, null, tableroImpreso);
	}

	public void setEsferaSelected(Esfera esferaSelected) {
		this.esferaSelected = esferaSelected;
	}

	public Esfera getEsferaSelected() {
		return esferaSelected;
	}

	public void setPersonajeSelected(Personaje personajeSelected) {
		this.personajeSelected = personajeSelected;
	}

	public Personaje getPersonajeSelected() {
		return personajeSelected;
	}

	public Tablero(int limiteX, int limiteY) {
		this.esferas = new ArrayList<Esfera>();
		this.personajes = new ArrayList<Personaje>();

		this.limite = new Coordenada(limiteX, limiteY);

		this.tableroImpresor = new TableroPrint(this);
		this.tableroImpreso = this.toString();

		Coordenada c = new Coordenada(0, 0);
		Personaje p = new Personaje("Goku", c, 15, this);
		this.agregarPersonaje(p);
		Coordenada c1 = new Coordenada(1, 0);
		Personaje p1 = new Personaje("Bulma", c1, 1, this);
		this.agregarPersonaje(p1);
		this.renderEsferas();
	}

	protected void ubicarUnPersonajeEnCoord(Personaje p, Coordenada c) {
		this.personajes.add(p);
		this.setTableroImpreso(this.toString());
	}

	protected void ubicarEsferaEnCoord(Esfera e, Coordenada c) {
		if (this.hayLugar(c)) {
			// e.setCoordenada(c);
			this.esferas.add(e);
			this.setTableroImpreso(this.toString());
		}
	}

	public Coordenada ubicarAlgo(Random ran) {

		int x = this.limite.getX();
		int y = this.limite.getY();

		int cx = ran.nextInt(x);
		int cy = ran.nextInt(y);

		Coordenada coord = new Coordenada(y, x);

		while (!enLimite(coord)) {
			cx = ran.nextInt(x);
			cy = ran.nextInt(y);
			coord = new Coordenada(cy, cx);
		}
		return coord;
	}

	/**
	 * Este metodo lo que hace es renderizar las esferas en un lugar del tablero
	 * al azar.
	 * 
	 * @param ran
	 * @return
	 */
	public void renderEsferas() {
		Random ran = new Random();
		Coordenada c;
		int i = 0;
		while (i < 7) {
			int x = ran.nextInt(this.limite.getX());
			int y = ran.nextInt(this.limite.getY());

			c = new Coordenada(y, x);
			if (this.hayLugar(c)) {
				i++;
				Esfera e = new Esfera(c, i);
				this.agregarEsfera(e);
			}
		}
		this.setTableroImpreso(this.toString());
	}

	/**
	 * Revisa si en en la coordenada c pasada por parametro hay algun personaje
	 * y retorna un booleano, true si lo hay y false si no hay.
	 * 
	 * @param Coordenada
	 *            c
	 * @return boolean
	 */
	boolean hayPersonajes(Coordenada c) {
		boolean respuesta = false;
		for (Personaje p : this.personajes) {
			respuesta = respuesta || (p.estaEnLaCoordenada(c));
		}
		return respuesta;
	}

	/**
	 * Revisa si en en la coordenada coord pasada por parametro esta fuera o
	 * dentro del limite y retorna un booleano, true si lo esta y false si no.
	 * 
	 * @param Coordenada
	 *            coord
	 * @return boolean
	 */
	boolean enLimite(Coordenada coord) {

		return coord.getX() >= 0 && coord.getX() < limite.getX()
				&& coord.getY() >= 0 && coord.getY() < limite.getY();

	}

	/**
	 * Revisa si en en la coordenada x pasada por parametro hay algun
	 * "obstaculo" y retorna un booleano, true si lo hay y false si no hay.
	 * 
	 * @param Coordenada
	 *            x
	 * @return boolean
	 */
	public boolean hayLugar(Coordenada a) {
		return !hayEsferas(a) && enLimite(a) && !hayPersonajes(a);
	}

	/**
	 * Revisa si en en la coordenada x pasada por parametro hay una esfera y
	 * retorna un booleano, true si lo hay y false si no hay.
	 * 
	 * @param Coordenada
	 *            x
	 * @return boolean
	 */
	boolean hayEsferas(Coordenada x) {

		boolean respuesta = false;
		for (Esfera e : this.esferas) {
			Coordenada coord = e.getCoordenada();
			respuesta = respuesta || coord.equals(x);
		}

		return respuesta;
	}

	public String toString() {
		this.tableroImpresor.dibujarTablero(this);
		return this.tableroImpresor.imprimirTablero();
	}

	/**
	 * Este metodo retorna el personaje que esta en la coordenada c, si es que
	 * hay alguno, de lo contrario, devuelve null.
	 * 
	 * @param Coordenada
	 *            c
	 * @return Auto
	 */
	Personaje enCoordenada(Coordenada c) {

		Personaje per = null;
		int i = 0;
		for (Personaje p : this.personajes) {
			if (p.estaEnLaCoordenada(c)) {
				per = p;
				return per;
			}
			i++;
		}
		i = 0;
		return per;
	}

	public void agregarPersonaje(Personaje p) {
		if (!this.hayLugar(p.getCoordenada())) {
			throw new UserException(
					"No se puede colocar al personaje en esa ubicación");
		}
		p.setMapa(this);
		this.ubicarUnPersonajeEnCoord(p, p.getCoordenada());
		this.firePropertyChange("personajes", null, p);
		this.elementos.add(p);
		this.firePropertyChange("elementos", null, p);
	}

	public void agregarEsfera(Esfera e) {
		this.ubicarEsferaEnCoord(e, e.getCoordenada());
		this.firePropertyChange("esferas", null, e);
		this.elementos.add(e);
		this.firePropertyChange("elementos", null, e);
	}

	public static void main(String[] args) {

		/*
		 * Tablero t= new Tablero(8, 8); Random ran = new Random();
		 * t.renderEsferas(ran); System.out.println(t.toString());
		 */

	}

	public void quitarEsfera(Posicionable e) {
		this.getEsferas().remove(e);
		ArrayList<Posicionable> elemAct = this.getElementos();
		elemAct.remove(e);
		this.firePropertyChange("elementos", null, elemAct);
		this.setTableroImpreso(this.toString());

	}

	public void quitarPersonaje(Posicionable p) {
		this.getPersonajes().remove(p);
		ArrayList<Posicionable> elemAct = this.getElementos();
		elemAct.remove(p);
		this.firePropertyChange("elementos", this.getElementos(), elemAct);
		this.setTableroImpreso(this.toString());

	}

	public void quitarElemento(Posicionable p) {

		if (p.getClass() == Esfera.class) {
			this.quitarEsfera(p);
		} else {
			this.quitarPersonaje(p);
		}
		this.firePropertyChange("elementos", null, this.getElementos());

	}

}
